package com.miranteinfo.seam.jsf.listener;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;

import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;

import org.ajax4jsf.component.AjaxViewRoot;
import org.ajax4jsf.component.html.HtmlAjaxSupport;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.context.AjaxContextImpl;
import org.ajax4jsf.event.EventsQueue;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.contexts.Contexts;

@Name("com.miranteinfo.SkipRequiredValidationListener")
public class SkipRequiredValidationListener {
	
	private static final String SKIP_VALIDATION_ATTRIBUTE_NAME = "miranteSeamSkipRequiredValidation";
	private static final String REQUIRED_VALIDATION_SKIPED = "miranteSeamRequiredValidationSkiped";

	@Observer("org.jboss.seam.beforePhase")
	public void beforePhase(PhaseEvent phaseEvent) {
		if(phaseEvent.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS)){
			
			FacesContext facesContext = phaseEvent.getFacesContext();
			AjaxContext ajaxContext = AjaxContextImpl.getCurrentInstance(facesContext);
			
			if(ajaxContext.isAjaxRequest()){
				try{
					
					EventsQueue eventsQueue = getAjaxEventsQueue(facesContext);
					FacesEvent facesEvent = getLastEvent(eventsQueue);
					if(facesEvent != null){
						
						UIComponent component = facesEvent.getComponent();
						
						if(component instanceof HtmlAjaxSupport && component.getAttributes().containsKey(SKIP_VALIDATION_ATTRIBUTE_NAME)){
							if(component.getParent() instanceof UIInput){
								UIInput input = (UIInput)component.getParent();
								if(input.isRequired()){
									input.setRequired(false);
									Contexts.getEventContext().set(REQUIRED_VALIDATION_SKIPED, input.getClientId(facesContext));
								}
							}
						}
					}
					
				}catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	@Observer("org.jboss.seam.afterPhase")
	public void afterPhase(PhaseEvent phaseEvent) {
		FacesContext facesContext = phaseEvent.getFacesContext();

		if(phaseEvent.getPhaseId().equals(PhaseId.PROCESS_VALIDATIONS) && Contexts.getEventContext().isSet(REQUIRED_VALIDATION_SKIPED)){
			((UIInput)facesContext.getViewRoot().findComponent((String)Contexts.getEventContext().get(REQUIRED_VALIDATION_SKIPED))).setRequired(true);
		}
	}
	
	/**
	 * Recupera a fila de eventos ajax. 
	 * 
	 * @param facesContext
	 * @return
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 */
	private EventsQueue getAjaxEventsQueue(FacesContext facesContext) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException{
		AjaxViewRoot viewRoot = (AjaxViewRoot)facesContext.getViewRoot(); 
		Method getAjaxEventsQueue = AjaxViewRoot.class.getDeclaredMethod("getAjaxEventsQueue");
		getAjaxEventsQueue.setAccessible(true);
		return (EventsQueue)getAjaxEventsQueue.invoke(viewRoot);
	}
	
	/**
	 * Recupera o ultimo  evento da fila de eventos ajax.
	 * 
	 * @param eventsQueue
	 * @return
	 */
	private FacesEvent getLastEvent(EventsQueue eventsQueue){
		if(eventsQueue.isEmpty()) return null;
		
		Iterator<FacesEvent> it = eventsQueue.iterator();
		FacesEvent facesEvent = null;
		while(it.hasNext()) facesEvent = it.next();
		
		return facesEvent;
	}
}
